
#include "common.h"

#include "InputData.h"
#include "Matrix.h"
#include "MatrixMath.h"
#include "ConsumptionModel.h"
#include "CM_AES_SBox_1.h"
#include "Timer.h"

void help();
Matrix* ComputeR(Matrix*,Matrix*);

int main(int argc, char *argv[]) {
	InputData* InputFile;
	string file;
	ConsumptionModel* cm;
	REALN* dummy;
	Matrix *SF,*Tracks;
	Matrix *R;
	Timer* t1 = new Timer();
	short end_loop;
	UINTN read_sample,old_sample,current_sample;
	Matrix* R_full;
	
	if(MEMORY==DEVICE_MEM){//cuda initialization
		CUDA_SC(cudaMalloc((void**)&dummy,0));
		CUDA_SC(cudaFree(dummy));
	}
	MatrixMath::ShowMem();
	
	/*
	Matrix* m1 = new Matrix(5,4,CPU_MEM);
	Matrix* m2;
	Matrix* m3 = new Matrix(6,5,CPU_MEM);
	Matrix* m4;
	Matrix* m5;
	
	m1->setGrow(1.0,1.0);
	m1->transpose();
	m2=new Matrix(m1,DEVICE_MEM);
	m3->setGrow(1.0,1.0);
	m3->transpose();
	m4=new Matrix(m3,DEVICE_MEM);
	m5=MatrixMath::Mult(m2,m4);
	m2->print();
	m4->print();
	m5->print();
	exit(33);
	//175 ... 1720
	*/
	
	/*Matrix* m1 = new Matrix(5,4,DEVICE_MEM);
	Matrix* m2;
	m1->setGrow(1.0,2.0);
	m1->save("m1.bin");
	m2 = new Matrix("m1.bin");
	m1->print();
	delete(m1);
	m2->print();
	exit(33);*/
	
	//Matrix::fast_compare("R_cpu.bin","R_device.bin",0.01,7);	exit(33);
	/*cout<<"cpu"<<endl;
	R = new Matrix("R_cpu.bin");
	for(UINTN i=0;i<R->getRow();i++){
		for(UINTN j=0;j<R->getCol();j++){
			if(R->get(i,j)>0.00025){
				cout<<i<<" "<<j<<" "<<R->get(i,j)<<endl;
			}
		}
	}
	delete(R);
	cout<<"device"<<endl;
	R = new Matrix("R_device.bin");
	for(UINTN i=0;i<R->getRow();i++){
		for(UINTN j=0;j<R->getCol();j++){
			if(R->get(i,j)>0.00025){
				cout<<i<<" "<<j<<" "<<R->get(i,j)<<endl;
			}
		}
	}
	exit(33);*/
	
	/*R = new Matrix(3,2,CPU_MEM);
	R->setGrow(0.0,1.0);
	R_full = new Matrix(3,10,CPU_MEM);
	R_full->append(R,0,2);
	R_full->append(R,2,4);
	R_full->print();
	exit(33);*/
	
	//Matrix::fast_compare("R_full_cpu.bin","R_full_old.bin",0.005,5);exit(33);
	/*R = new Matrix("R_full_device.bin");
	for(UINTN i=0;i<R->getRow();i++){
		for(UINTN j=0;j<R->getCol();j++){
			if(fabs(R->get(i,j))>0.0005){
				cout<<i<<" "<<j<<" "<<R->get(i,j)<<endl;
			}
		}
	}
	exit(33);*/
	
	if(argc != 2){//TODO config file
		cout<<"Invalid command line"<<endl;
		help();
		exit(3);
	}
	if(1){//TODO config file
		cm = new CM_AES_SBox_1();
	}
	
	file = argv[1];
	InputFile = new InputData(file);
	//TODO mem limit
	//TODO mem type
	R_full = new Matrix(256,InputFile->getNSample(),CPU_MEM);
	//Matrix::SaveStub("R_full.bin",256,InputData->getNSample());
	SF = InputFile->ReadTexts(cm,MEMORY);
	SF->printInfo("SF");
	//Texts->print();
	
	//TODO better memory estimation (CPU/GPU)
	read_sample = (AVAILABLE_GPU_MEM/(sizeof(REALN)*256*8))/INPUT_PADDING*INPUT_PADDING;
	cout<<"computing block: "<<read_sample<<"x"<<InputFile->getNTrack()<<endl;
	if(read_sample<InputFile->getNSample()){
		current_sample = read_sample;
	}else{
		current_sample = InputFile->getNSample();
	}
	old_sample = 0;
	end_loop=false;
	do{
		if(current_sample>InputFile->getNSample()){
			current_sample=InputFile->getNSample();
			end_loop=true;
		}
		Tracks = InputFile->ReadTracks(MEMORY,old_sample,current_sample);
		//TODO allign(Tracks), filtering(Tracks, filter) from configuration
		R = ComputeR(SF,Tracks);
		//R_full->append(R,old_sample,current_sample);
		delete(R);
		old_sample=current_sample;
		current_sample += read_sample;
	}while(end_loop==false); 
	
	R_full->printInfo("R_full");
	//R_full->save("R_full_cpu.bin");
	
	delete(SF);
	delete(R_full);	
	MatrixMath::ShowMem();
	cout<<"--- END ---"<<endl;
}
Matrix*	ComputeR(Matrix* SF,Matrix* Tracks){
	UINTN ntrack;
	Matrix *temp,
	*Sum_consumption,*Sum_consumption2,*Mean_consumption,*Var_consumption,
	*Sum_hypothesis,*Sum_hypothesis2,*Mean_hypothesis,*Var_hypothesis,
	*Var_conj,*Sum_cons_times_hyp_T,
	*t1,*t2,*t3,*R;
	
	ntrack = Tracks->getCol();
	Tracks->transpose();
	Sum_cons_times_hyp_T = MatrixMath::Mult(SF,Tracks);
	Tracks->transpose();
	
	Sum_consumption = MatrixMath::ReduceToCol(Tracks);
	temp = MatrixMath::AllElemUnaryOp(Tracks,OP_POW2,0.0);
	Sum_consumption2 = MatrixMath::ReduceToCol(temp);
	delete(temp);
	delete(Tracks);
	Mean_consumption = MatrixMath::AllElemUnaryOp(Sum_consumption,OP_MULT,1/(REALN)ntrack);
	Var_consumption = MatrixMath::Var(Sum_consumption,Sum_consumption2,Mean_consumption,ntrack);
	delete(Sum_consumption2);
	
	Sum_hypothesis = MatrixMath::ReduceToCol(SF);
	temp = MatrixMath::AllElemUnaryOp(SF,OP_POW2,0.0);
	Sum_hypothesis2 = MatrixMath::ReduceToCol(temp);
	delete(temp);
	Mean_hypothesis = MatrixMath::AllElemUnaryOp(Sum_hypothesis,OP_MULT,1/(REALN)ntrack);
	Var_hypothesis = MatrixMath::Var(Sum_hypothesis,Sum_hypothesis2,Mean_hypothesis,ntrack);
	delete(Sum_hypothesis2);

	Var_hypothesis->transpose();
	Var_conj = MatrixMath::Mult(Var_hypothesis,Var_consumption);
	delete(Var_hypothesis);
	delete(Var_consumption);
	
	Mean_consumption->transpose();
	t1 = MatrixMath::Mult(Mean_hypothesis,Mean_consumption);
	
	Sum_consumption->transpose();
	t3 = MatrixMath::Mult(Mean_hypothesis,Sum_consumption);
	delete(Mean_hypothesis);
	delete(Sum_consumption);
	
	t2 = MatrixMath::Mult(Sum_hypothesis,Mean_consumption);
	delete(Sum_hypothesis);
	delete(Mean_consumption);
	
	R = MatrixMath::R(Sum_cons_times_hyp_T,t1,t2,t3,Var_conj,ntrack);
	
	delete(t1);
	delete(t2);
	delete(t3);
	delete(Var_conj);
	delete(Sum_cons_times_hyp_T);
	
	/*
	Matrix* comp = Var_conj;
	Matrix* temp2 = new Matrix(comp,CPU_MEM);
	Matrix* temp = new Matrix("Vc1_device.bin");
	Matrix* tempc = new Matrix(temp,CPU_MEM);
	tempc->print(10);
	cout<<endl;
	temp2->print(10);
	cout << (*tempc)%(*temp2)<<endl;
	*/
	//Var_conj->save("Vc1.bin");
	
	//Sum_hypothesis->print(10);
	//Mean_hypothesis->print(10);
	
	return R;
}

void help(){
	cout<<"...help..."<<endl; //TODO help
}



